import itertools
import os
from pathlib import Path

import pytest

from storage.blobstore.fileblob import FileBlobStore, EncryptionAlgorithm, CompressMethod
from storage.kvstore.pickle import PickleKVStore
from storage.mutex import Mutex


@pytest.fixture
def blob_store_path():
    path = Path('testdata.blob.bin')
    yield str(path)
    if path.exists():
        path.unlink()


@pytest.fixture
def kv_store_path():
    path = Path('testdata.kv.bin')
    yield str(path)
    if path.exists():
        path.unlink()


alg128 = [EncryptionAlgorithm.AES128CTR, EncryptionAlgorithm.AES128CBC]
secret128 = b'0123456701234567'
secret256 = b'01234567012345670123456701234567'


@pytest.mark.parametrize('alg,compr,secret', [
    (alg, compr, secret128 if alg in alg128 else secret256)
    for alg, compr in itertools.product(EncryptionAlgorithm.__members__.values(), CompressMethod.__members__.values())
])
def test_get_put_blob(kv_store_path, blob_store_path, alg, compr, secret):
    kv_store = PickleKVStore(kv_store_path)
    store = FileBlobStore(blob_store_path, kv_store, encrypt=alg, secret=secret, compress=compr)
    store.put_blob(b'key', b'data')
    assert store.get_blob(b'key') == b'data'


def test_put_blob_unknown_encryption_alg(kv_store_path, blob_store_path):
    kv_store = PickleKVStore(kv_store_path)
    with pytest.raises(Exception, match='is not a valid'):
        store = FileBlobStore(blob_store_path, kv_store, secret=secret128, encrypt=100)
        store.put_blob(b'key', b'data')


def test_put_blob_unknown_compress_method(kv_store_path, blob_store_path):
    kv_store = PickleKVStore(kv_store_path)
    with pytest.raises(Exception, match='is not a valid'):
        store = FileBlobStore(blob_store_path, kv_store, secret=secret128, compress=100)
        store.put_blob(b'key', b'data')


@pytest.mark.benchmark
@pytest.mark.parametrize('mutex_type', [None, Mutex])
def test_put_blob_benchmark_interprocess_sync_enabled(benchmark, kv_store_path, blob_store_path, mutex_type):
    kv_store = PickleKVStore(kv_store_path)
    blob_store = FileBlobStore(blob_store_path, kv_store, secret=secret128, mutex_type=mutex_type)

    def benchmark_put_blob():
        for _ in range(100):
            blob_store.put_blob(os.urandom(8), os.urandom(512))

    benchmark(benchmark_put_blob)
